
#ifndef __M_SINK_H__
#define __M_SINK_H__

#include "util.hpp"
#include <utility>
#include <memory>
#include <fstream>
#include <cassert>
#include <sstream>
#include <string>
namespace PRJLOG
{
    class LogSink
    {
    public:
        using ptr = std::shared_ptr<LogSink>;
        LogSink() {}
        virtual ~LogSink() {}
        virtual void Log(const char *data, size_t len) = 0;
    };
    // 落地方向：标准输出
    class StdoutSink : public LogSink
    {
    public:
        // 将日志写到标准输出
        void Log(const char *data, size_t len) override
        {
            std::cout.write(data, len);
        }
    };
    // 落地方向：指定文件
    class FileSink : public LogSink
    {
    public:
        // 构造时传入指定文件名，并将文件打开，将文件句柄管理起来
        FileSink(const std::string &pathname) : _pathname(pathname)
        {
            // 创建文件所在目录
            util::file::createDirectory(util::file::path(pathname));
            // 创建目录下的文件
            _ofs.open(pathname, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }
        // 将日志写到指定文件
        void Log(const char *data, size_t len) override
        {
            _ofs.write(data, len);
            assert(_ofs.good());
        }

    private:
        std::string _pathname;
        std::ofstream _ofs;
    };
    // 落地方向：滚动文件(以文件大小滚动)
    class RollbysizeSink : public LogSink
    {
    public:
        RollbysizeSink(const std::string &basename, size_t max_size) : _basename(basename), _max_fsize(max_size), _cur_fsize(0), _file_count(0)
        {
            std::string pathname = CreateNFile();
            // 创建文件所在目录
            util::file::createDirectory(util::file::path(pathname));
            // 创建目录下的文件
            _ofs.open(pathname, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }
        // 将日志输出到滚动文件中
        void Log(const char *data, size_t len) override
        {
            if (_cur_fsize >= _max_fsize)
            {
                _ofs.close();
                std::string pathname = CreateNFile();
                _ofs.open(pathname, std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
                _cur_fsize = 0;
            }
            _ofs.write(data, len);
            assert(_ofs.good());
            _cur_fsize += len;
        }

    private:
        // 创建文件,返回完整的文件名
        std::string CreateNFile()
        {
            // 根据时间创建文件
            struct tm lt;
            time_t t = util::Date::nowtime();
            localtime_r(&t, &lt);
            std::stringstream ss;
            ss << _basename;
            ss << (lt.tm_year + 1900);
            ss << (lt.tm_mon + 1);
            ss << (lt.tm_mday);
            ss << (lt.tm_hour);
            ss << (lt.tm_min);
            ss << (lt.tm_sec);
            ss << "-";
            ss << _file_count++;
            ss << ".log";
            return ss.str();
        }

    private:
        size_t _file_count;
        std::string _basename; //./basefile-
        std::ofstream _ofs;
        size_t _max_fsize; // 文件最大大小，超过这个大小需要换一个文件
        size_t _cur_fsize; // 文件当前大小
    };

    // 落地方向：滚动文件(根据时间滚动)
    // 实现思想：根据时间段的不同来创建文件
    class RollbytimeSink : public LogSink
    {
    public:
        enum class GapType
        {
            Gap_sec = 1,
            Gap_min,
            Gap_hour,
            Gap_day
        };
        RollbytimeSink(const std::string &basename, GapType gap) : _basename(basename)
        {
            switch (gap)
            {
            case GapType::Gap_sec:
                _gap = 1;
                break;
            case GapType::Gap_min:
                _gap = 60;
                break;
            case GapType::Gap_hour:
                _gap = 60 * 60;
                break;
            case GapType::Gap_day:
                _gap = 60 * 60 * 60;
                break;
            default:
                break;
            }
            std::string pathname = CreateNFile();
            // 创建文件所在目录
            util::file::createDirectory(util::file::path(pathname));
            // 创建目录下的文件
            _ofs.open(pathname, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
            _cur_gap=util::Date::nowtime()/_gap;
        }
        // 将日志输出到滚动文件中
        void Log(const char *data, size_t len) override
        {
            if((util::Date::nowtime()/_gap)!=_cur_gap)
            {
                _ofs.close();
                std::string pathname = CreateNFile();
                _ofs.open(pathname, std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
                _cur_gap=util::Date::nowtime()/_gap;
            }
            _ofs.write(data, len);
            assert(_ofs.good());
        }

    private:
        // 创建文件,返回完整的文件名
        std::string CreateNFile()
        {
            // 根据时间创建文件
            struct tm lt;
            time_t t = util::Date::nowtime();
            localtime_r(&t, &lt);
            std::stringstream ss;
            ss << _basename;
            ss << (lt.tm_year + 1900);
            ss << (lt.tm_mon + 1);
            ss << (lt.tm_mday);
            ss << (lt.tm_hour);
            ss << (lt.tm_min);
            ss << (lt.tm_sec);
            ss << "-";
            ss << _file_count++;
            ss << ".log";
            return ss.str();
        }

    private:
        size_t _file_count;
        std::string _basename; //./basefile-
        std::ofstream _ofs;
        size_t _cur_gap; // 当前时间段
        size_t _gap;
    };

    class SinkFactory
    {
    public:
        template <typename SinkType, typename... Args>
        static LogSink::ptr create(Args &&...args)
        {
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    };
}

#endif